﻿using AutoMapper;
using Ducu.BLL.Entity;
using Ducu.GLB.Global;
using Ducu.SRV.Query;
using Ducu.SRV.ServiceInteface;
using Ducu.SRV.ViewModel.Plan;
using Ducu.SRV.ViewModel.Plan.EditorTemplates;
using Ducu.SRV.ViewModel.Plan.Shared;
using Ducu.SRV.ViewModel.Shared;
using Ducu.SRV.ViewModel.Task.Shared;
using Global.Core.ExtensionMethod;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Ducu.SRV.ProdService
{
    public class PlanService : BaseService, IPlanService
    {
        public int New(NewModel model, IEnumerable<int> DaysInWeek)
        {
            Plan plan = Mapper.Map<Plan>(model.PublishContent);

            plan.Vower = getCurrentUser();
            setKeywords(plan, model.PublishContent);

            repoFactory.Save<Plan>(plan);

            return plan.Id;
        }

        public void Edit(_NewOrEditModel model, int planId)
        {
            if (IsCurrentUserVower(planId))
            {
                Plan plan = repoFactory.Load<Plan>(planId);
                Mapper.Map(model, plan);

                setKeywords(plan, model);
            }
        }

        private void setKeywords(Plan plan, _NewOrEditModel model)
        {
            //always clear the original keywords first
            plan.KeyWords = new List<Keyword>();

            IList<string> strKeywords = model.KeyWords.ToListKeywords();
            foreach (var item in strKeywords)
            {
                Keyword exist = repoFactory.Query<Keyword>()
                    .GetByContent(item).SingleOrDefault();
                if (exist == null)
                {
                    plan.KeyWords.Add(new Keyword { Content = item });
                }
                else
                {
                    plan.KeyWords.Add(exist);
                }
            }

        }

        public JoinResult Join(int planId)
        {
            User current = getCurrentUser();
            Plan plan = repoFactory.Load<Plan>(planId);

            if (plan.StillNeedSupervisors())
            {
                plan.JoinedBy(current);
                return JoinResult.Succeed;
            }
            else
            {
                return JoinResult.FailForFull;
            }
        }

        public _NeedFulfilModel GetNeedFulfil()
        {
            User current = getCurrentUser();

            var allPlans = repoFactory.Query<Plan>()
                .MyFulfil(current).NoDue().NotExpire();
            IList<Plan> plans = new List<Plan>();
            TaskService taskService = new TaskService();
            foreach (var plan in allPlans)
            {
                IList<_DayModel> days = taskService.getPrevious(plan, plan.Expire);
                if (days.Where(d => d.Status == Status.WaitComplete).Count() > 0)
                {
                    plans.Add(plan);
                }
            }

            _NeedFulfilModel model = new _NeedFulfilModel
            {
                Items = new List<NameIdModel>()
            };

            foreach (var item in plans)
            {
                model.Items.Add(Mapper.Map<NameIdModel>(item));
            }

            return model;
        }

        public EditModel GetEdit(int planId)
        {
            Plan plan = repoFactory.Load<Plan>(planId);

            _NewOrEditModel publish = Mapper.Map<_NewOrEditModel>(plan);
            publish.OptionalNumbersOfSupervisors = getOptionalNumberOfSupervisors(plan);

            return new EditModel { Origin = publish };
        }

        private IList<int> getOptionalNumberOfSupervisors(Plan plan)
        {
            IList<int> result = new List<int>();

            //even the minimum can not be 0
            int begin = 1;
            if (!plan.Supervisors.IsNullOrEmpty())
            {
                begin = plan.Supervisors.Count;
            }

            for (int i = begin; i <= Constant.ALLOWED_MAX_SUPERVISORS_NUMBER; i++)
            {
                result.Add(i);
            }
            return result;
        }

        public ShowModel GetShow(int planId)
        {
            Plan plan = repoFactory.Load<Plan>(planId);

            _ShowItemModel content = new _ShowItemModel
            {
                Title = Mapper.Map<NameIdModel>(plan)
            };


            return new ShowModel
            {
                PublishContent = content,
            };
        }

        public _ShowItemBodyModel GetShowItemBody(int planId)
        {
            Plan plan = repoFactory.Load<Plan>(planId);

            _ShowItemBodyModel model = Mapper.Map<_ShowItemBodyModel>(plan);

            model.ShowButton = new List<Can>();

            User current = getCurrentUser();
            if (current != null)
            {
                if (isCurrentUser(plan.Vower))
                {
                    TaskService taskService = new TaskService();
                    if (taskService.GetFulfil(plan.Id).NeedFulfil)
                    {
                        model.ShowButton.Add(Can.Fulfil);
                    }
                    model.ShowButton.Add(Can.Edit);
                }
                else if (plan.Supervisors.Where(s => s.Id == current.Id).SingleOrDefault() != null)
                {
                    if (repoFactory.Query<Task>().GetNeedAccepted(plan).Count() > 0)
                    {
                        model.ShowButton.Add(Can.Accept);
                    }
                }
                else
                {
                    if (plan.StillNeedSupervisors())
                    {
                        model.ShowButton.Add(Can.Join);
                    }
                }
            }
            else
            {
                //NOTE: it is NOT duplicated with the above
                if (plan.StillNeedSupervisors())
                {
                    model.ShowButton.Add(Can.Join);
                }
            }

            return model;
        }

        public bool IsCurrentUserVower(int id)
        {
            Plan plan = repoFactory.Load<Plan>(id);

            bool result = getCurrentUser() != null
                && plan.Vower.Id == getCurrentUser().Id;

            if (!result)
            {
                //TODO: need log, send message, ect.

            }

            return result;
        }


        public _NeedAcceptModel GetNeedAccept()
        {
            User current = getCurrentUser();
            _NeedAcceptModel model = new _NeedAcceptModel
            {
                Items = new List<NameIdModel>()
            };
            var tasks = repoFactory.Query<Task>().GetNeedAccepted(current);
            var plans = from task in tasks
                        group task by task.Plan into planGroup
                        select planGroup;

            foreach (var item in plans)
            {
                model.Items.Add(Mapper.Map<NameIdModel>(item.Key));
            }

            return model;
        }


        public int GetCount(bool due, bool expire, bool full, string my, string keyword, int? userId)
        {
            return get(due, expire, full, my, keyword, userId).Count();
        }

        public IndexModel Get(bool due, bool expire, bool full, string my, string keyword, int? userId, Pager pager)
        {
            var plans = get(due, expire, full, my, keyword, userId)
                .OrderByDescending(p => p.CreateTime)
                .Page<Plan>(pager);

            IndexModel model = new IndexModel
            {
                Items = new List<_ShowItemModel>()
            };

            foreach (var plan in plans)
            {
                model.Items.Add(new _ShowItemModel
                {
                    Title = Mapper.Map<NameIdModel>(plan)
                });
            }

            return model;
        }

        private IQueryable<Plan> get(bool due, bool expire, bool full, string my, string keyword, int? userId)
        {
            User user;
            if (userId.HasValue)
            {
                user = repoFactory.Load<User>(userId.Value);
            }
            else
            {
                user = getCurrentUser();
            }

            var result = repoFactory.Query<Plan>();

            ///check the keyword first: 
            ///if there is no such keyword, then return null directly
            if (!string.IsNullOrEmpty(keyword))
            {
                IList<Keyword> objKeywords = new List<Keyword>();
                IList<string> strKeywords = keyword.ToListKeywords();

                foreach (var k in strKeywords)
                {
                    Keyword objKeyword = repoFactory.Query<Keyword>()
                        .GetByContent(k).SingleOrDefault();

                    ///Note: here is the reason why no getKeywords() method
                    ///it's a performance hit
                    if (objKeywords == null)
                    {
                        return null;
                    }
                    else
                    {
                        objKeywords.Add(objKeyword);
                        result = result.Keyword(objKeyword);
                    }
                }
            }

            if (!due)
            {
                result = result.NoDue();
            }
            if (!expire)
            {
                result = result.NotExpire();
            }
            if (!full)
            {
                result = result.NeedSupervisors();
            }
            if (!string.IsNullOrEmpty(my))
            {
                if (my == UrlParameter.Fulfil)
                {
                    result = result.MyFulfil(user);
                }
                else if (my == UrlParameter.Supervise)
                {
                    result = result.MySupervisor(user);
                }
                else
                {
                    //TODO: 
                    throw new Exception("the parameter my is expire: " + my);
                }
            }

            return result;
        }


        public bool CanLeave(int planId, DateTime date)
        {
            Plan plan = repoFactory.Load<Plan>(planId);
            if (plan.DaysOfLeave <= 0)
            {
                return false;
            }
            else
            {
                int hasLeft = repoFactory.Query<Task>()
                    .GetByPlan(repoFactory.Load<Plan>(planId))
                    .Between(date.GetBeginInCurrentWeek(), 
                        date.GetEndInCurrentWeek()).HasLeft().Count();
                return hasLeft < plan.DaysOfLeave;
            }
        }


        public void Stop(int id)
        {
            Plan plan = repoFactory.Load<Plan>(id);
            plan.To = DateTime.Now.Date;
        }
    }

    internal static class PlanServiceExtension
    {
        internal static IList<string> ToListKeywords(this string source)
        {
            return source.Split(' ')
                .Where(k => !string.IsNullOrEmpty(k))
                .ToList();
        }
    }
}
